/**
 * tjftp -- A simple FTP server, using the ANSI C
 * Copyright (C) 2011  degui.chen <degui.chen@qq.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "sockets.h"
#include "memory.h"
#include <stdio.h>
static int debut_mode = 0;
static char debug_log_path[] = "/tmp/socket_log.txt";
static FILE* fp;
static void _printf_debug_info() {
	printf("\n\n-----------------socket log path : %s\n\n", debug_log_path);
	if (fp != NULL) {
		fclose(fp);
	}
}
void tj_sockets_debug() {
	if (!debut_mode) {
		debut_mode = 1;
		fp = fopen(debug_log_path, "w");
		if (fp == NULL) {
			perror(debug_log_path);
			exit(-1);
		}
		atexit(_printf_debug_info);
	}
}
int _tj_sockets_open(int domain, int type, int protocol, const char* file, const char* fun, int line, int ansi_c) {
	int fd_socket;
	if ((fd_socket = socket(domain, type, protocol)) == -1) {
		perror("socket()");
		exit(1);
	}
	if (debut_mode) { //收集调试数据
		char* debug = (char*) tj_memory_alloc(255);
		sprintf(debug, "open  socket(%i), type(%i), protocol(%i), file(%s), function(%s), line(%i), ANSI C(%i)\n", fd_socket, type, protocol, file, fun, line, ansi_c);
		fputs(debug, fp);
	}
	return fd_socket;
}
void _tj_sockets_close(int fd, const char* file, const char* fun, int line, int ansi_c) {
	if (fd > 0) {
		close(fd);
		if (debut_mode) {
			char* debug = (char*) tj_memory_alloc(255);
			sprintf(debug, "close socket(%i), file(%s), function(%s), line(%i), ANSI C(%i)\n", fd, file, fun, line, ansi_c);
			fputs(debug, fp);
		}
	}
}
int _tj_accepts_open(int fd_socket, struct sockaddr * addr, unsigned int* addr_len, const char* file, const char* fun, int line, int ansi_c) {
	int fd_accept;
	if ((fd_accept = accept(fd_socket, addr, addr_len)) == -1) {
		perror("accept() error\n");
		printf("socket(%i) \n", fd_socket);
		return -1;
	}
	if (debut_mode) { //收集调试数据
		char* debug = (char*) tj_memory_alloc(255);
		sprintf(debug, "open  accept(%i), file(%s), function(%s), line(%i), ANSI C(%i)\n", fd_accept, file, fun, line, ansi_c);
		fputs(debug, fp);
	}
	return fd_accept;
}

void _tj_accepts_close(int fd, const char* file, const char* fun, int line, int ansi_c) {
	if (fd > 0) {
		close(fd);
		if (debut_mode) {
			char* debug = (char*) tj_memory_alloc(255);
			sprintf(debug, "close accept(%i), file(%s), function(%s), line(%i), ANSI C(%i)\n", fd, file, fun, line, ansi_c);
			fputs(debug, fp);
		}
	}
}

//设置超时时间
void tj_set_timeout(int fd_socket, int timeout) {
	struct timeval tv;
	tv.tv_sec = timeout;
	tv.tv_usec = 0;
	if (-1 == setsockopt(fd_socket, SOL_SOCKET, SO_SNDTIMEO, (char*) &tv, sizeof(tv))) {
		perror("options_set_timeout");
		exit(1);
	}
	if (-1 == setsockopt(fd_socket, SOL_SOCKET, SO_RCVTIMEO, (char*) &tv, sizeof(tv))) {
		perror("options_set_timeout");
		exit(1);
	}
}
//多次重复使用地址
void tj_set_reuse(int fd_socket) {
	int option = 1;
	if (-1 == setsockopt(fd_socket, SOL_SOCKET, SO_REUSEADDR, (const char*) &option, sizeof(option))) {
		perror("options_set_reuse");
		exit(1);
	}
#ifdef SO_REUSEPORT
	if (-1 == setsockopt(fd_socket, SOL_SOCKET, SO_REUSEPORT, (const char*) &option, sizeof(option))) {
		perror("options_set_reuse");
		exit(1);
	}
#endif
}

//设置发送和接收缓冲区大小
void tj_set_buffer(int fd_socket, int value) {
	if (-1 == setsockopt(fd_socket, SOL_SOCKET, SO_SNDBUF, (char *) &value, sizeof(int))) {
		perror("options_set_buffer");
		exit(1);
	}
	if (-1 == setsockopt(fd_socket, SOL_SOCKET, SO_RCVBUF, (char *) &value, sizeof(int))) {
		perror("options_set_buffer");
		exit(1);
	}
}
//设置广播
void tj_set_broadcast(int fd_socket) {
	int option = 1;
	if (-1 == setsockopt(fd_socket, SOL_SOCKET, SO_BROADCAST, (char *) &option, sizeof(int))) {
		perror("options_set_broadcast");
		exit(1);
	}
}

//设置控制IP头部数据
void tj_set_ip_including(int fd_socket) {
	int option = 1;
	if (-1 == setsockopt(fd_socket, IPPROTO_IP, IP_HDRINCL, (char *) &option, sizeof(int))) {
		perror("options_set_ip_including");
		exit(1);
	}
}

char* tj_get_local_ip(void) {
	int fd = socket(AF_INET, SOCK_DGRAM, 0);
	if (fd < 0)
		return "";
	struct ifreq request[16];
	struct ifconf ifconfig;
	ifconfig.ifc_len = sizeof(request);
	ifconfig.ifc_buf = (caddr_t) request;
	if (ioctl(fd, SIOCGIFCONF, (char *) &ifconfig))
		return "";
	int length = ifconfig.ifc_len / sizeof(struct ifreq);
	while (length-- > 0 && ioctl(fd, SIOCGIFADDR, (char *) &request[length]))
		;
	close(fd);
	return inet_ntoa(((struct sockaddr_in*) (&request[length].ifr_addr))-> sin_addr);
}

void tj_get_remote_address(int port, char* ip, struct sockaddr_in* result) {
	struct sockaddr_in address = *result;
	bzero(&address, sizeof(address));
	address.sin_family = AF_INET;
	address.sin_port = htons(port);
	struct hostent *host;
	if ((host = gethostbyname(ip)) == NULL) {
		perror("gethostbyname()");
		exit(1);
	}
	address.sin_addr = *((struct in_addr*) host->h_addr_list[0]);
	*result = address;
}

void tj_get_local_address(int port, struct sockaddr_in* result) {
	struct sockaddr_in address = *result;
	bzero(&address, sizeof(address));
	address.sin_family = AF_INET;
	address.sin_port = htons(port);
	address.sin_addr.s_addr = htonl(INADDR_ANY);
	*result = address;
}

int tj_create_tcp_socket() {
	int fd_socket = tj_sockets_open(AF_INET, SOCK_STREAM, 0);
	tj_set_timeout(fd_socket, 3);
	tj_set_reuse(fd_socket);
	return fd_socket;
}
void tj_tcp_open_connect(int fd, char* ip, int port) {
	struct sockaddr_in address;
	tj_get_remote_address(port, ip, &address);
	if (connect(fd, (struct sockaddr *) &address, sizeof(struct sockaddr)) == -1) {
		perror("connect()");
		printf("socket(%i), ip(%s), port(%i)\n", fd, ip, port);
		exit(1);
	}
}
void tj_tcp_open_listen(int fd_socket, int port, int backlog) {
	struct sockaddr_in address_server;
	tj_get_local_address(port, &address_server);
	if (bind(fd_socket, (struct sockaddr *) &address_server, sizeof(struct sockaddr)) == -1) {
		perror("bind()");
		exit(1);
	}
	if (listen(fd_socket, backlog) == -1) {
		perror("listen() error\n");
		exit(1);
	}
}
int tj_tcp_accept(int fd_socket) {
	int address_length = sizeof(struct sockaddr_in);
	struct sockaddr_in address_client;
	int fd_accept;
	if ((fd_accept = accept(fd_socket, (struct sockaddr *) &address_client, &address_length)) == -1) {
		//perror("accept()");
		return -1;
	}
	return fd_accept;
}
void tj_tcp_send_file(int fd, char* path) {
	FILE *stream = fopen(path, "rb");
	if (stream == NULL) {
		perror("Error opening file");
	} else {
		fseek(stream, 0, SEEK_SET);
		char line[256];
		while (NULL != fgets(line, 256, stream)) {
			send(fd, line, strlen(line), 0);
		}
		fclose(stream);
	}
}
int tj_udp_create_socket() {
	int fd_socket = tj_sockets_open(AF_INET, SOCK_DGRAM, 0);
	tj_set_timeout(fd_socket, 3);
	tj_set_reuse(fd_socket);
	return fd_socket;
}
int tj_udp_bind(int fd_socket, int port) {
	struct sockaddr_in address_server;
	tj_get_local_address(port, &address_server);
	if (bind(fd_socket, (struct sockaddr *) &address_server, sizeof(struct sockaddr)) == -1) {
		perror("Bind error.");
		return -1;
	}
	return 0;
}
